package action

import (
	"math"
	"strconv"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"go.uber.org/atomic"
	"gorm.io/gorm"

	"ssalarm/common/constant"
	"ssalarm/common/dto"
	"ssalarm/common/enums"
	"ssalarm/common/middleware/mqtt"
	"ssalarm/common/utils"
	"ssalarm/internal/alarm/models"
)

type Handle struct {
	Orm *gorm.DB
}

func (action *Handle) Execute(topic string, jsonData map[string]interface{}) {
	sn := jsonData["sn"].(string)
	dataMap := jsonData["data"].(map[string]interface{})
	//msgType := int(jsonData["msgType"].(float64))
	cacheAlarmConfig, _ := env.Cache.HashGetAll(constant.AlarmConfigKey + sn)
	if len(cacheAlarmConfig) == 0 {
		return
	}
	alarmList := make([]models.SysAlarm, 0)
	autoDoneList := make([]models.SysAlarm, 0)
	// 拉平k.kk形式
	flattenDataMap := map[string]interface{}{}
	utils.FlattenJsonWithRoot(dataMap, flattenDataMap, []string{}, map[string]interface{}{})
	//需要进行消息通知的记录
	noticeArr := make([]map[string]interface{}, 0)
	//告警计数刷新缓存
	refresh := make(map[string]interface{})
	//遍历当前的实时数据
	for k, v := range flattenDataMap {
		//获取当前属性对应的告警配置集合
		confArray, currentVal := getKeyAlarmConf(k, v, cacheAlarmConfig)
		//如果不存在 跳过属性告警判断
		if len(confArray) == 0 {
			continue
		}
		//判断属性告警配置缓存是否存在变动(内部记录变动)
		flag := atomic.NewBool(false)
		//遍历属性的所有类型的告警配置
		for index, confJson := range confArray {
			//属性告警配置是否有效
			if !checkConfigIsEffect(confJson) {
				continue
			}
			// 0：不告警 1：越上限 2：越下限 3: 越上下限 4: 等于 5: 事件
			warningMode := utils.GetIntValueFiled(confJson, "warning_mode")
			//判断告警触发机制 1: 首次  2: 每次
			triggerType := utils.GetIntValueFiled(confJson, "triggerType")
			// 产生告警的次数(针对triggerType值为1时有效)
			tmpCount := utils.GetIntValueFiled(confJson, "tmpCount")
			//判断是否自动处理
			autoDone := utils.GetBoolValueField(confJson, "autoDone")
			//判断是否满足告警
			isAlarm := chargeAlarm(warningMode, currentVal, confJson)
			//每个设备每种属性每个告警类型
			tmpKey := sn + "|" + k + "|" + strconv.Itoa(warningMode)
			// 未达到告警条件
			if !isAlarm {
				// 告警恢复-自动处理机制
				hasAlarm, _ := env.Cache.HashGet(constant.HasAlarmRowCache, tmpKey)
				// 有历史告警、当前恢复、且配置为自动恢复
				if len(hasAlarm) > 0 && autoDone {
					// 自动恢复的告警更新为已处理
					item := formatAlarmItem(sn, currentVal, k, confJson)
					autoDoneList = append(autoDoneList, item)
					if triggerType == 1 {
						confJson["tmpCount"] = 0
						//次数刷新
						flag.Store(true)
						confArray[index] = confJson
					}
					// 自动恢复的告警需通知相应用户
					alarmAutoDoneNotify(item)
				}
				//标记清理
				_ = env.Cache.HashDel(constant.HasAlarmRowCache, tmpKey)
				continue
			}
			// 为空的不管
			if triggerType == 1 {
				if tmpCount >= 1 {
					//跳过当前告警配置的处理逻辑
					continue
				}
				//缓存告警次数(首次告警应该为 0 + 1 )
				confJson["tmpCount"] = tmpCount + 1
				//次数刷新
				flag.Store(true)
			}
			// 已告警条目缓存
			_ = env.Cache.HashSet(constant.HasAlarmRowCache, tmpKey, "true")
			//获取每个告警对象
			alarmItem := formatAlarmItem(sn, currentVal, k, confJson)
			alarmList = append(alarmList, alarmItem)
			//获取每个消息通知对象
			notifyItem := formatNotifyItem(alarmItem, currentVal)
			//通知集合缓存
			noticeArr = append(noticeArr, notifyItem)
			// 事件明细缓存
			cnt, _ := env.Cache.LPush(constant.DevEventCache, string(utils.MapToBytes(map[string]interface{}{
				"sn":    alarmItem.DevSn,
				"title": utils.If(warningMode == 5, "设备事件", "设备告警").(string),
				"name":  alarmItem.AlarmType,
				"type":  utils.If(warningMode == 5, 2, 3).(int),
				"time":  utils.GetNowTimeStr(),
			})))
			if cnt > 1000 {
				// 保留近1000条
				_, _ = env.Cache.RPop(constant.DevEventCache)
			}
			//更新map缓存
			if flag.Load() {
				//存在变动
				confArray[index] = confJson
			}
		}
		//当前缓存存在变动处理 需要更新
		if flag.Load() {
			//缓存
			refresh[k] = string(utils.MapArrToBytes(confArray))
		}
	}
	// 推送
	if len(noticeArr) > 0 {
		//告警消息通知
		mqtt.MqttClient.Publish(constant.AlarmNotifyTopic, string(utils.MapArrToBytes(noticeArr)))
		//清空
		noticeArr = make([]map[string]interface{}, 0)
	}
	// 次数
	if len(refresh) > 0 {
		//批量刷新变动的告警配置
		_ = env.Cache.HashSet(constant.AlarmConfigKey+sn, refresh)
		//清空
		refresh = make(map[string]interface{})
	}
	// 告警记录插入or更新
	sysAlarm := &models.SysAlarm{}
	sysAlarm.Orm = action.Orm
	if len(alarmList) > 0 {
		if err := sysAlarm.Add(&alarmList); err != nil {
			logger.Error(err)
		} else {
			logger.Info("告警记录入库", len(alarmList))
			sysDataDetailModel := &models.SysDataDetail{}
			sysDataDetailModel.Orm = action.Orm
			if err = sysDataDetailModel.Add(&models.SysDataDetail{
				Type:       "DATA_ALARM_NUM",
				Key:        sn,
				Value:      len(alarmList),
				CreateTime: dto.LocalTime(time.Now()),
			}); err != nil {
				env.Log.Error(err)
			}
		}
	}
	if len(autoDoneList) > 0 {
		sysAlarm.AutoDoneAlarmList(autoDoneList)
	}
}

// 获取实时属性对应的告警配置集合
func getKeyAlarmConf(docKey string, docVal interface{}, fv map[string]string) ([]map[string]interface{}, string) {
	//格式处理
	strVal := utils.InterfaceToStr(docVal)
	//获取每个属性对应的告警最新配置和数据
	if confArrayStr, ok := fv[docKey]; ok {
		//转换为对应的map集合(如果无效那么为空)
		return utils.BytesToMapArr([]byte(confArrayStr)), strVal
	} else {
		//不存在配置
		return []map[string]interface{}{}, strVal
	}
}

// 判断属性告警配置是否有效
func checkConfigIsEffect(confJson map[string]interface{}) bool {
	if _, ok := confJson["warning_mode"]; !ok {
		return false
	}
	if _, ok := confJson["warning_type"]; !ok {
		return false
	}
	return true
}

// 判断是否触发高级
func chargeAlarm(warningMode int, currentValStr string, confJson map[string]interface{}) bool {
	if warningMode == 5 { // 事件直接告警
		return true
	}
	currentVal, err := strconv.ParseFloat(currentValStr, 64)
	if err != nil {
		logger.Error(err)
		return false
	}
	//阈值配置
	thresholdMax := utils.GetFloatValueFiled(confJson, "warning_ulimit")
	thresholdMin := utils.GetFloatValueFiled(confJson, "warning_dlimit")
	thresholdEq := utils.GetFloatValueFiled(confJson, "warning_elimit")
	if warningMode == 1 { // 越上限
		return currentVal > thresholdMax
	} else if warningMode == 2 { // 越下限
		return currentVal < thresholdMin
	} else if warningMode == 3 { // 越上下限
		// 支持写反 ...
		max := math.Max(thresholdMax, thresholdMin)
		min := math.Min(thresholdMax, thresholdMin)
		return currentVal < max && currentVal > min
	} else if warningMode == 4 { // 等于
		return currentVal == thresholdEq
	}
	return false
}

// 初始化告警入库对象
func formatAlarmItem(sn string, currentVal string, docKey string, confJson map[string]interface{}) models.SysAlarm {
	now := dto.LocalTime(time.Now())
	name := utils.GetStringValueFiled(confJson, "name")
	deptId, _ := env.Cache.HashGet(constant.DeviceDeptCache, sn)
	devCache, _ := env.Cache.HashGet(constant.DeviceEasyCache, sn)
	sysAlarm := models.SysAlarm{
		Id:            utils.SimpleUUID(),
		DevSn:         sn,
		VarKey:        docKey,
		VarName:       utils.If(len(name) > 0, name, docKey).(string),
		VarValue:      currentVal,
		Level:         utils.GetIntValueFiled(confJson, "warning_level"),
		ProcessStatus: false,
		AlarmTime:     &now,
		AlarmType:     utils.GetStringValueFiled(confJson, "alarmType"),
		DeptId:        deptId,
		Reason:        utils.GetStringValueFiled(confJson, "alarmReason"),
		Suggestion:    utils.GetStringValueFiled(confJson, "suggestion"),
		CreateTime:    &now,
		UpdateTime:    &now,
		AlarmMode:     utils.GetIntValueFiled(confJson, "warning_mode"),
	}
	devMap := utils.BytesToMap([]byte(devCache))
	if devType, ok := devMap["type"]; ok {
		sysAlarm.DevType = devType.(string)
	}
	if devName, ok := devMap["name"]; ok {
		sysAlarm.DevName = devName.(string)
	}
	return sysAlarm
}

// 自动恢复的告警消息通知
func alarmAutoDoneNotify(item models.SysAlarm) {
	notifyItem := formatNotifyItem(item, item.VarValue)
	//告警自动恢复
	notifyItem["isAutoDone"] = true
	//消息通知
	// 暂不发恢复通知了
	//mqtt.MqttClient.Publish(constant.AlarmNotifyTopic, string(utils.MapToBytes(notifyItem)))
}

// 消息通知对象
func formatNotifyItem(alarmItem models.SysAlarm, currentVal string) map[string]interface{} {
	//每个item
	json := make(map[string]interface{})
	json["sn"] = alarmItem.DevSn
	json["devType"] = alarmItem.DevType
	json["devName"] = alarmItem.DevName
	json["timestamp"] = utils.GetNowTimeStr()
	json["source"] = enums.Alarm.Get().Source
	json["deptId"] = alarmItem.DeptId
	//当前告警值
	json["currentVal"] = currentVal
	//告警的属性
	json["variable"] = alarmItem.VarKey
	json["variableName"] = alarmItem.VarName
	json["alarmLevel"] = alarmItem.Level
	json["alarmType"] = alarmItem.AlarmType
	//设备属性
	json["originId"] = alarmItem.Id
	json["remark"] = ""
	//返回
	return json
}
